// © Головин Г.Г., Логика игрового процесса, 2023
'use strict';
// игровое поле и его размеры
let field, rows = 20, columns = 10;
// ускорение падения фигур
const REDUCTION_STEP = 25;
const REDUCTION_SNAIL = 5;
let reduction = REDUCTION_STEP;
// скорость падения фигур
const START_DELAY = 600;
const MIN_DELAY = 80;
let stepDelay = START_DELAY;
let sleepTimeout, rapidFall = false;
// уровень, следующий уровень, счёт
let level, nextLevel, score;
// массив фигур тетрамино
const figures = FIGURE.set();
// текущая фигура, следующая фигура
let currentFigure, nextFigure;
// статусы игры
const GAME = {RUN:0,LEVEL:1,PAUSE:2,OVER:3};
// текущий статус
let status = GAME.PAUSE;
// подготовить новую игру
function prepareNewGame() {
  field = [];
  for (let i = 0; i < rows; i++) {
    field[i] = [];
    for (let j = 0; j < columns; j++)
      field[i][j] = 0;
  }
  status = GAME.PAUSE;
  level = 0;
  score = 0;
  nextLevel = (10 * columns) * (level + 1);
  stepDelay = START_DELAY;
  startFigureFall();
  repaint();
}
// начало падения фигуры
function startFigureFall() {
  currentFigure = nextFigure;
  const x = Math.floor(columns/2+columns%2-1);
  if (isFreeSpace(0, x)) {
    doPlaceFigure(0, x);
    repaint();
  } else {
    status=GAME.OVER;
    for (let y=-1; y>-4; y--)
      if (isFreeSpace(y, x, false)) {
        doPlaceFigure(y, x);
        break;
      }
    repaint();
    return;
  }
  const rnd = Math.floor(Math.random() * figures.length);
  nextFigure = figures[rnd].clone();
  if (status==GAME.LEVEL) return;
  setTimeout(figureFall, 60);
}
// падение фигуры
function figureFall() {
  if (rapidFall)
    stepDown();
  else
    sleepTimeout = setTimeout(stepDown, stepDelay);
}
// шаг вниз
function stepDown() {
  sleepTimeout = undefined;
  if (status==GAME.PAUSE) return;
  if (isSpaceDown()) {
    doStepDown();
    setTimeout(figureFall, 60);
  } else {
    mergeFigure(currentFigure.type);
    let fullRows = 0;
    for (let i = 0; i < rows; i++)
      if (isFullRow(i)) {
        for (let c = 3; c >= 0; c--)
          setTimeout(blinkFullRow, 400 * fullRows + 100 * c, i, c);
        setTimeout(slideDown, 400 * fullRows + 400, i);
        fullRows++;
      }
    score += columns * [0,1,3,5,10][fullRows];
    if (score >= nextLevel) {
      level++;
      status = GAME.LEVEL;
      nextLevel = (10 * columns) * (level + 1);
      stepDelay = Math.max(MIN_DELAY,stepDelay-reduction);
    }
    startFigureFall();
  }
}
// свободное место ниже текущей фигуры
function isSpaceDown() {
  for (let y = rows-2; y >= 0; y--)
    for (let x = 0; x < columns; x++)
      if (field[y][x]==11 && field[y+1][x]>0 && field[y+1][x]<11
            || field[y+1][x]==11 && y==rows-2)
        return false;
  return true;
}
// сдвинуть текущую фигуру вниз
function doStepDown() {
  for (let y = rows-2; y >= 0; y--)
    for (let x = 0; x < columns; x++)
      if (field[y][x]==11) {
        field[y][x]=0;
        field[y+1][x]=11;
      }
  repaint();
}
// завершение движения текущей фигуры
function mergeFigure(type) {
  for (let x = 0; x < columns; x++)
    for (let y = 0; y < rows; y++)
      if (field[y][x] == 11)
        field[y][x] = type;
}
// заполненная строка
function isFullRow(row) {
  for (let x = 0; x < columns; x++)
    if (field[row][x] == 0)
      return false;
  return true;
}
// моргание заполненной строки
function blinkFullRow(row, color) {
  for (let x = 0; x < columns; x++)
    field[row][x] = color;
  repaint();
}
// сдвинуть поле вниз
function slideDown(row) {
  for (let y = row-1; y >= 0; y--)
    for (let x = 0; x < columns; x++)
      if (field[y+1][x]!=11 && field[y][x]!=11)
        field[y+1][x]=field[y][x];
  repaint();
}
// свободное место для текущей фигуры в пределах границ поля
function isFreeSpace(y, x, fullSize=true) {
  const height = currentFigure.shape.length;
  const wight = currentFigure.shape[0].length;
  if (fullSize && (y<0 || y+height>rows || x<0 || x+wight>columns))
    return false;
  for (let yy=0; yy<height; yy++)
    for (let xx=0; xx<wight; xx++)
      if (currentFigure.shape[yy][xx]>0)
        if (y+yy>=0 && y+yy<rows && x+xx>=0 && x+xx<columns)
          if (field[y+yy][x+xx]!=0)
            return false;
  return true;
}
// разместить текущую фигуру в пределах границ поля
function doPlaceFigure(y, x, type=11) {
  const height = currentFigure.shape.length;
  const wight = currentFigure.shape[0].length;
  for (let yy=0; yy<height; yy++)
    for (let xx=0; xx<wight; xx++)
      if (currentFigure.shape[yy][xx]>0)
        if (y+yy>=0 && y+yy<rows && x+xx>=0 && x+xx<columns)
          field[y+yy][x+xx]=type;
}
// для вызова из контроллера
function moveFigureLeft() {
  if (isSpaceLeft())
    doStepLeft();
  repaint();
}
// свободное место слева от текущей фигуры
function isSpaceLeft() {
  for (let x = 1; x < columns; x++)
    for (let y = 0; y < rows; y++)
      if (field[y][x]==11 && field[y][x-1]>0 && field[y][x-1]<11
            || field[y][x-1]==11 && x==1)
        return false;
  return true;
}
// сдвинуть текущую фигуру влево
function doStepLeft() {
  for (let x = 1; x < columns; x++)
    for (let y = 0; y < rows; y++)
      if (field[y][x]==11) {
        field[y][x]=0;
        field[y][x-1]=11;
      }
}
// для вызова из контроллера
function moveFigureRight() {
  if (isSpaceRight())
    doStepRight();
  repaint();
}
// свободное место справа от текущей фигуры
function isSpaceRight() {
  for (let x = columns-2; x >=0; x--)
    for (let y = 0; y < rows; y++)
      if (field[y][x]==11 && field[y][x+1]>0 && field[y][x+1]<11
            || field[y][x+1]==11 && x==columns-2)
        return false;
  return true;
}
// сдвинуть текущую фигуру вправо
function doStepRight() {
  for (let x = columns-2; x >=0; x--)
    for (let y = 0; y < rows; y++)
      if (field[y][x]==11) {
        field[y][x]=0;
        field[y][x+1]=11;
      }
}
// для вызова из контроллера, поворот фигуры
function rotateFigure() {
  let y = rows, x = columns;
  for (let yy = 0; yy < rows; yy++)
    for (let xx = 0; xx < columns; xx++)
      if (field[yy][xx] == 11) {
        if (y > yy) y = yy;
        if (x > xx) x = xx;
      }
  if (y == rows || x == columns) return;
  const old = currentFigure.shape;
  doPlaceFigure(y, x, 0);
  currentFigure.rotate();
  if (isFreeSpace(y, x))
    doPlaceFigure(y, x);
  else if (isFreeSpace(y, x-1))
    doPlaceFigure(y, x-1);
  else {
    currentFigure.shape = old;
    doPlaceFigure(y, x);
  }
  repaint();
}
// после загрузки всех частей страницы, запускаем игру
document.addEventListener('DOMContentLoaded', function() {
  addEventListener('keydown', keyPressed);
  addEventListener('keyup', keyReleased);
  const rnd = Math.floor(Math.random() * figures.length);
  nextFigure = figures[rnd].clone();
  prepareNewGame();
});
